#pragma once


#include <fstream>

// the abstract data stream for read for write.
class DataStream
{
public:
	DataStream(bool save) : mSaving(save) {}
	virtual ~DataStream() {}

	bool IsSaving() const { return mSaving; }
	bool IsLoading() const { return !mSaving; }

	virtual bool Good() const = 0;
	virtual size_t Read(void* buff, size_t num) = 0;
	virtual size_t Write(const void* buff, size_t num) = 0;
	virtual bool Skip(int off) = 0;
	virtual bool Eof() const = 0;

private:
	bool mSaving;
};

// the file data stream for read for write.
class FileDataStream : public DataStream
{
public:
	FileDataStream(const char* fileName, bool save)
		: DataStream(save)
	{
		if (IsSaving())
			mFileStream.open(fileName, std::ios_base::out | std::ios_base::binary);
		else
			mFileStream.open(fileName, std::ios_base::in | std::ios_base::binary);
	}
	virtual ~FileDataStream() { Close(); }

	void Close() { mFileStream.close(); }

	virtual bool Good() const { return mFileStream.good(); }
	virtual bool Eof() const { return mFileStream.eof(); }

	virtual size_t Read(void* buff, size_t num)
	{
		mFileStream.read((char*)buff, num);
		return static_cast<size_t>(mFileStream.gcount());
	}

	virtual size_t Write(const void* buff, size_t num)
	{
		mFileStream.write((const char*)buff, num);
		return static_cast<size_t>(mFileStream.gcount());
	}

	virtual bool Skip(int off)
	{
		mFileStream.seekg(off, std::ios_base::cur);
		return mFileStream.good();
	}

private:
	std::fstream mFileStream;
};

// the memory data stream for read for write.
// it just for reading.
class MemeoryDataStream : public DataStream
{
public:
	MemeoryDataStream(const void* buff, size_t size)
		: DataStream(false), mBuff(buff), mSize(size), mPos(0)
	{ }

	virtual bool Good() const { return mPos < mSize; }
	virtual bool Eof() const { return mPos == mSize; }

	virtual size_t Read(void* buff, size_t num)
	{
		num = Min(num, mSize - mPos);
		memcpy(buff, (const char*)mBuff + mPos, num);
		mPos += num;
		return num;
	}

	virtual size_t Write(const void* buff, size_t num)
	{
		throw std::runtime_error("MemeoryDataStream doesn't support write!!!");
		return 0;
	}
	
	virtual bool Skip(int off)
	{
		if (mPos + off >= mSize)
			return false;
		mPos += off;
		return true;
	}

private:
	const void* mBuff;
	size_t mSize;
	size_t mPos;
};
